package com.sff.site.abstract {
	import com.sff.site.core.INode;
	import com.sff.site.core.IPath;
	import com.sff.site.events.NodeEvent;
	
	import flash.external.ExternalInterface;
	import flash.net.URLVariables;

	public class AbstractPath implements IPath {

		
		public static const SEPARATOR 			: String = '/';
		public static const PARAM_SEP 			: String = '?';
		public static const DEVICE_SEPARATOR 	: String = ':';
		
		public function AbstractPath( path : String, device : String = null, params : URLVariables = null  ) {
			_device = device;
			_solve( path );
			
			if( params && _params ) throw new Error( "AbstractPath : params defined both in path and constructor param" );
			if( params ) _params = params;
		}
		
		
		public function appendDefaults() : IPath {
			var n : INode = cleanup().toNode();
			var npath : IPath = n.path();
			if( n && n.getDefaultId() ) 
				if( ! npath.append(n.getDefaultId()).equals( this ) )
					return npath.append(n.getDefaultId()).appendDefaults();
			return this;
		}
		
		public function  cleanup() : IPath {
			return null;
		}
		
		public function toString() : String {
			return ( ( _device ) ? _device + DEVICE_SEPARATOR + SEPARATOR : "") + _path;
		}

		public function nodeExist() : Boolean {
			if( ! _nodes ) _nodes = _solveNodes( );
			return _valid;
		}

		public function append( path : String ) : IPath {
			return null;
		}

		public function clone() : void {
			// TODO stub
		}

		
		public function getPath() : String {
			return _path;
		}
		

		public function getDevice() : String {
			return _device;
		}
		
		public function getParams() : URLVariables {
			return _params;
		}
		
		public function setParams(params : URLVariables) : void {
			_params = params;
		}

		public function segments() : Array {
			if( ! _segments ) _segments = _buildSegments( );
			return _segments;
		}

		public function nodes( until : INode = null ) : Array {
			if( ! _nodes ) _nodes = _solveNodes();
			if( until ) {
				var untilIndex : int = _nodes.indexOf( until );
				if( untilIndex == -1 ) return new Array().concat( _nodes );
				return _nodes.slice( untilIndex );
			}
			return new Array().concat( _nodes );
		}

		public function isRoot() : Boolean {
			return true;
		}

		public function toNode( ) : INode {
			var na : Array = nodes( );
			if( ! _valid ) return null;
			return na[ na.length - 1 ];
		}

		public function makeAbsolute() : IPath {
			return null;
		}

		public function makeRelative() : IPath {
			return null;
		}
		
		public function equals(path : IPath) : Boolean {
			if( path.getDevice() == null || _device == null )
				return ( path.getPath() == getPath() );
			return ( path.toString() == toString() );
		}
		
		
		public function diff( other : IPath ) : INode {
			var oNodes : Array = other.nodes();
			var ns : Array = nodes();
			var minL : int=  Math.min( oNodes.length, ns.length );
			for ( var i : int = 0; i < minL ; i += 1 )
				if( oNodes[i] != ns[i] ) return oNodes[ i - 1 ];
						
			return oNodes[ i - 1 ];
		}
			
		public function contain(path : IPath) : Boolean {
			var onodes : Array = path.nodes();
			var tnodes : Array = nodes();
			var l : int = onodes.length;
			for (var c : int = 0; c < l; c+=1 )
				if( onodes[ c ] != tnodes[ c ] ) return false;
				
			return true;
		}

		private function _solve(path : String) : void {
			
			var pSplit : Array = path.split( PARAM_SEP );
			path = pSplit[0];
			
			if( pSplit.length == 2 ) {
				_params = new URLVariables( pSplit[1] );
			}
			
			var dSplit : Array = pSplit[0].split(DEVICE_SEPARATOR + SEPARATOR );
			if( dSplit.length == 1 ) {
				path = path;
			} else if( dSplit.length == 2 ) {
				
				ExternalInterface.call('console.error', path);
				
				//if( _device != null && _device != dSplit[0] ) {
				//	throw new Error( "Invalid path - different device id in constructor and path's string : " + path );
				//}
				
				path = dSplit[1];
				_device = dSplit[0];
				
			} else { 
				throw new Error( "Invalid path - more than one Device delimiter in path : " + path );
			}	
				
			path = path.replace( STRIP_PATH , "$2" );
			
			_path = path;
		}

		private function _buildSegments() : Array {
			if( _path == "" ) return [];
			var a : Array = _path.split( SEPARATOR );
			return a;
		}

		private function _solveNodes() : Array {
			
			var ns : Array = new Array( );
			var segs : Array = segments( );
			
			var currNode : INode = _getRootNode( );
			ns.push( currNode );
			var l : int = segs.length;
			var i : int = - 1;
			_valid = true;
			while( ++ i < l ) {
				if( ! currNode.hasChild( segs[i] ) ) {
					_valid = false;
					break;	
				}
				currNode = currNode.getChild( segs[i] );
				ns.push( currNode );
			}
			ns[ ns.length - 1 ].addEventListener( NodeEvent.CHILD_ADDED, onChildAdded, false, 200, true );
			return ns;
		}
		
		private function onChildAdded(event : NodeEvent) : void {
			while( _nodes.length > 0 )
				_nodes.pop().removeEventListener( NodeEvent.CHILD_ADDED, onChildAdded );
			
			_nodes = null;
		}

		protected function _getRootNode() : INode {
			// abstract;
			return null;
		}

		
		protected var _path 	: String;
		protected var _device 	: String;
		protected var _params 	: URLVariables;

		//_____________________________________________________________________________
		//																 cached results

		private var _segments : Array;
		private var _nodes : Array;
		private var _valid : Boolean;

		
		protected static const STRIP_PATH : RegExp = new RegExp( "^(" + SEPARATOR + "*)(([^" + SEPARATOR + "]+" + SEPARATOR + ")*[^" + SEPARATOR + "]+)?(" + SEPARATOR + "*)$" , "gim" );
		
	}
}
